Spread.Viewsでは、インラインエディタをカスタマイズすることで、複雑な編集関数を処理できます。組み込みエディタでは、プリミティブなデータ型しか編集できませんが、インラインエディタをカスタマイズすると、複合的なデータ型を持つフィールドを編集したり、編集ロジックをカスタマイズしたりできます。
サンプルコードでは、以下のカスタムエディタ型が使用されています。
- NameEditor - 姓、名、またはミドルネームなど、短いテキストフィールドの編集に使用します。
- RadioButtonEditor - ブール値の選択に使用します。
- TextAreadEditor - 長文入力の編集に使用します。
インラインエディタをカスタマイズするには、次の手順を実行します。
サンプルコード
以下の手順では、グリッドの初期化と列の定義を終えていることを前提としています。詳細については、「基本的なグリッドの作成」および「列の定義」を参照してください。
DIVタグのグリッドIDを使用してコードを初期化し、allowEditingプロパティをtrueに設定します。
var dataView = new GC.Spread.Views.DataView(document.getElementById('grid1'), data, columns, new GC.Spread.Views.Plugins.GridLayout({ allowEditing: true })); function NameEditor(options) { this.options = options; }
NameEditorの実装を指定する関数を定義します。
//Function for Name Editor NameEditor.prototype = { init: function() { var options = this.options; var container = options.container; var rawValue = options.value; var rawNames = rawValue.split(' '); var firstName = ''; var lastName = ''; var middleName = ''; var hasMiddleName = false; switch (rawNames.length) { case 0: firstName = ''; lastName = ''; break; case 1: firstName = rawNames[0]; lastName = ''; break; case 2: firstName = rawNames[0]; lastName = rawNames[1]; break; default: firstName = rawNames[0]; lastName = rawNames[rawNames.length - 1]; middleName = rawNames.slice(1, rawNames.length - 1).join(' '); hasMiddleName = true; } this.defaultValue = rawValue; setAttributes(container, { 'styles': { 'padding': '3px', 'border': '1px solid #0066FF', 'background': 'white', 'border-radius': '2px' } }); container.innerHTML = '<div style="margin-bottom:6px"><div><i>First Name:</i></div><input type="text" class="firstname" style="width:120px" value="' + firstName + '"></div>' + (hasMiddleName ? '<div style="margin-bottom:6px"><div><i>Middle Name:</i></div><input type="text" class="middlename" style="width:120px" value="' + middleName + '"></div>' : '') + '<div><div><i>Last Name:</i></div><input type="text" class="lastname" style="width:120px" value="' + lastName + '"></div>'; this.firstNameInput = container.querySelector("input.firstname"); this.middleNameInput = container.querySelector("input.middlename"); this.lastNameInput = container.querySelector("input.lastname"); this.focus(); }, focus: function() { this.firstNameInput.focus(); }, serialize: function() { return this.middleNameInput ? this.firstNameInput.value + " " + this.middleNameInput.value + " " + this.lastNameInput.value : this.firstNameInput.value + " " + this.lastNameInput.value; } };
RadioButtonEditorの実装を指定する関数を定義します。
//Function for Radio Editor function RadioButtonEditor(options) { var self = this; self.options = options; } RadioButtonEditor.prototype = { init: function() { var options = this.options; var rawValue = options.value; var container = options.container; setAttributes(container, { 'styles': { 'min-width': '90px', 'padding': '6px', 'border': '1px solid #0066FF', 'border-radius': '2px' } }); var list = ['male', 'female']; var len = list.length; var i = 0; var checked = ""; var fragInnerHTML = ""; while (i < len) { checked = list[i] !== rawValue ? "" : checked = "checked"; fragInnerHTML += '<div><input type="radio" style="outline: 0" name="radioeditor" id="' + list[i] + '" value="' + list[i] + '"' + checked + '><LABEL style="font-weight:normal;padding: 4px;" for="' + list[i] + '">' + list[i] + '</LABEL></div> '; ++i; } container.innerHTML = fragInnerHTML; }, focus: function() { this.options.container.querySelector("input:checked").focus(); }, serialize: function() { return this.options.container.querySelector("input:checked").value; } }; function TextAreaEditor(options) { this.options = options; }
TextAreaEditorの実装を指定する関数を定義します。
//Function for TextArea Editor TextAreaEditor.prototype = { init: function() { var options = this.options; var container = options.container; container.innerHTML = '<textarea style="outline:0;width:100%;height:100%;border:0px;"></textarea>' + '<div style="width:100%">' + '<button class="cancel" style="float:right;width:60px;margin-right: 0.4em; background: #f1f1f1; border: solid 1px #e0e0e0;">Cancel</button>' + '<button class="save" style="float:right;width:60px;margin-right: 0.4em; background: #f1f1f1; border: solid 1px #e0e0e0;">Save</button>' + '</div>'; this.textArea = container.children[0]; this.cancelButton = container.querySelector("button.textarea-cancel"); this.saveButton = container.querySelector("button.textarea-save"); setAttributes(container, { 'styles': { 'padding': '3px', 'border': '1px solid #0066FF', 'border-radius': '2px' } }); this.textArea.value = options.value; this.textArea.focus(); this.clickHandler_ = this.containerClickHandler.bind(this); container.addEventListener('keydown', this.containerKeyDownHandler); container.addEventListener('click', this.clickHandler_); }, destroy: function() { var container = this.options.container; container.removeEventListener('keydown', this.containerKeyDownHandler); container.removeEventListener('click', this.clickHandler_); }, focus: function() { this.textArea.focus(); }, serialize: function() { return this.textArea.value; }, containerKeyDownHandler: function(e) { e.stopPropagation(); return false; }, containerClickHandler: function(e) { e.stopPropagation(); var target = e.target; var options = this.options; var dataview = options.dataview; if (target) { var tagName = target.tagName.toLowerCase(); if (tagName === 'button') { var className = target.className; if (className === 'save') { dataview.stopEditing(); } else if (className === 'cancel') { dataview.cancelEditing(); } } } } }; function setAttributes(element, attrs) { for (var key in attrs) { if (attrs.hasOwnProperty(key)) { var attr = attrs[key]; if (key === 'styles') { for (var prop in attr) { if (attr.hasOwnProperty(prop)) { element.style[prop] = attr[prop]; } } } else { element.setAttribute(key, attr); } } } }